#include "gtest/gtest.h"
#include "head.h"
#include <fstream>
#include <sstream>

// Test fixture for Sudoku class
class SudokuTest : public ::testing::Test {
protected:
    Sudoku sudoku;
};

// Test solveSudoku() function
TEST_F(SudokuTest, SolveSudoku) {
    Board board = {
            {'5', '3', '$', '$', '7', '$', '$', '$', '$'},
            {'6', '$', '$', '1', '9', '5', '$', '$', '$'},
            {'$', '9', '8', '$', '$', '$', '$', '6', '$'},
            {'8', '$', '$', '$', '6', '$', '$', '$', '3'},
            {'4', '$', '$', '8', '$', '3', '$', '$', '1'},
            {'7', '$', '$', '$', '2', '$', '$', '$', '6'},
            {'$', '6', '$', '$', '$', '$', '2', '8', '$'},
            {'$', '$', '$', '4', '1', '9', '$', '$', '5'},
            {'$', '$', '$', '$', '8', '$', '$', '7', '9'}
    };

    vector<Board> result = sudoku.solveSudoku(board);
    ASSERT_EQ(1, result.size()); // Only one solution expected

    Board expected = {
            {'5', '3', '4', '6', '7', '8', '9', '1', '2'},
            {'6', '7', '2', '1', '9', '5', '3', '4', '8'},
            {'1', '9', '8', '3', '4', '2', '5', '6', '7'},
            {'8', '5', '9', '7', '6', '1', '4', '2', '3'},
            {'4', '2', '6', '8', '5', '3', '7', '9', '1'},
            {'7', '1', '3', '9', '2', '4', '8', '5', '6'},
            {'9', '6', '1', '5', '3', '7', '2', '8', '4'},
            {'2', '8', '7', '4', '1', '9', '6', '3', '5'},
            {'3', '4', '5', '2', '8', '6', '1', '7', '9'}
    };

    ASSERT_EQ(expected, result[0]);
}

TEST_F(SudokuTest, SolveSudoku_UniqueSolution) {
    Board board = {
            {'5', '3', '$', '$', '7', '$', '$', '$', '$'},
            {'6', '$', '$', '1', '9', '5', '$', '$', '$'},
            {'$', '9', '8', '$', '$', '$', '$', '6', '$'},
            {'8', '$', '$', '$', '6', '$', '$', '$', '3'},
            {'4', '$', '$', '8', '$', '3', '$', '$', '1'},
            {'7', '$', '$', '$', '2', '$', '$', '$', '6'},
            {'$', '6', '$', '$', '$', '$', '2', '8', '$'},
            {'$', '$', '$', '4', '1', '9', '$', '$', '5'},
            {'$', '$', '$', '$', '8', '$', '$', '7', '9'}
    };

    vector<Board> result = sudoku.solveSudoku(board);
    ASSERT_EQ(1, result.size()); // Only one solution expected

    Board expected = {
            {'5', '3', '4', '6', '7', '8', '9', '1', '2'},
            {'6', '7', '2', '1', '9', '5', '3', '4', '8'},
            {'1', '9', '8', '3', '4', '2', '5', '6', '7'},
            {'8', '5', '9', '7', '6', '1', '4', '2', '3'},
            {'4', '2', '6', '8', '5', '3', '7', '9', '1'},
            {'7', '1', '3', '9', '2', '4', '8', '5', '6'},
            {'9', '6', '1', '5', '3', '7', '2', '8', '4'},
            {'2', '8', '7', '4', '1', '9', '6', '3', '5'},
            {'3', '4', '5', '2', '8', '6', '1', '7', '9'}
    };

    ASSERT_EQ(expected, result[0]);
}

TEST_F(SudokuTest, SolveSudoku_NoSolution) {
    Board board = {
            {'5', '3', '4', '6', '7', '8', '9', '1', '2'},
            {'6', '$', '7', '1', '9', '5', '$', '$', '$'},
            {'1', '9', '$', '3', '4', '2', '5', '6', '7'},
            {'8', '5', '9', '7', '$', '1', '4', '2', '3'},
            {'4', '2', '6', '8', '5', '3', '7', '9', '1'},
            {'7', '1', '3', '9', '2', '4', '8', '5', '6'},
            {'9', '6', '1', '5', '3', '7', '2', '8', '4'},
            {'2', '8', '7', '4', '1', '9', '6', '3', '5'},
            {'3', '4', '5', '2', '8', '6', '1', '7', '$'}
    };

    vector<Board> result = sudoku.solveSudoku(board);
    ASSERT_EQ(0, result.size()); // No solution expected
}


// Test checkBoard() function
TEST_F(SudokuTest, CheckBoard) {
    Board validBoard = {
            {'5', '3', '4', '6', '7', '8', '9', '1', '2'},
            {'6', '7', '2', '1', '9', '5', '3', '4', '8'},
            {'1', '9', '8', '3', '4', '2', '5', '6', '7'},
            {'8', '5', '9', '7', '6', '1', '4', '2', '3'},
            {'4', '2', '6', '8', '5', '3', '7', '9', '1'},
            {'7', '1', '3', '9', '2', '4', '8', '5', '6'},
            {'9', '6', '1', '5', '3', '7', '2', '8', '4'},
            {'2', '8', '7', '4', '1', '9', '6', '3', '5'},
            {'3', '4', '5', '2', '8', '6', '1', '7', '9'}
    };

    ASSERT_TRUE(sudoku.checkBoard(validBoard));

}


TEST_F(SudokuTest, GenerateBoardTest) {
    sudoku.initSudoku();
    Board board = sudoku.generateBoard(50, true);
    // 检查生成的棋盘是否合法
    EXPECT_TRUE(sudoku.checkBoard(board));
    // 检查生成的棋盘的空格是否等于50
    int cnt = 0;
    for (int i = 0; i < 9; i++) {
        for (int j = 0; j < 9; j++) {
            if (board[i][j] == '$') {
                cnt++;
            }
        }
    }
    EXPECT_EQ(cnt, 50);
}

#include <gtest/gtest.h>
#include "head.h"

// Test the initialization of Sudoku class
TEST_F(SudokuTest, InitSudokuTest) {
    Sudoku sudoku;
    sudoku.initSudoku();

    // Check if the public member function getRows returns the expected result
    for (int i = 0; i < 9; i++) {
        for (int j = 0; j < 9; j++) {
            EXPECT_EQ(sudoku.getRows()[i][j], 0);
        }
    }
}

// Test the flipping of values in Sudoku class
TEST_F(SudokuTest, FlipTest) {
    Sudoku sudoku;
    sudoku.initSudoku();

    // Test flipping a value at a specific position
    sudoku.flip(0, 3, 5);
    int i=0, j=3;
    // Check if the public member functions getRows, getColumns, and getBlocks return the expected results
    EXPECT_EQ(sudoku.getRows()[0][5], 1);
    EXPECT_EQ(sudoku.getColumns()[3][5], 1);
    EXPECT_EQ(sudoku.getBlocks()[(i / 3) * 3 + j / 3][5], 1);

    // Test flipping the same value again (should revert the flip)
    sudoku.flip(0, 3, 5);

    // Check if the public member functions getRows, getColumns, and getBlocks return the expected results
    EXPECT_EQ(sudoku.getRows()[0][5], 0);
    EXPECT_EQ(sudoku.getColumns()[3][5], 0);
    EXPECT_EQ(sudoku.getBlocks()[0][5], 0);
}

class FileTest : public ::testing::Test {
protected:
    Sudoku sudoku;
    std::string file_path = "test_input.txt";
};

TEST_F(FileTest, ReadFile) {
    // Prepare test input file
    std::ofstream outfile(file_path);
    outfile << "5 3 $ $ 7 $ $ $ $\n";
    outfile << "6 $ $ 1 9 5 $ $ $\n";
    outfile << "$ 9 8 $ $ $ $ 6 $\n";
    outfile << "8 $ $ $ 6 $ $ $ 3\n";
    outfile << "4 $ $ 8 $ 3 $ $ 1\n";
    outfile << "7 $ $ $ 2 $ $ $ 6\n";
    outfile << "$ 6 $ $ $ $ 2 8 $\n";
    outfile << "$ $ $ 4 1 9 $ $ 5\n";
    outfile << "$ $ $ $ 8 $ $ 7 9\n";
    outfile << "-----------------\n";
    outfile << "1 $ $ $ 2 $ $ $ $\n";
    outfile << "2 $ $ $ 1 $ 7 $ $\n";
    outfile << "$ $ $ 3 $ $ $ $ 8\n";
    outfile << "$ $ 9 $ $ $ $ $ $\n";
    outfile << "5 1 8 $ 6 3 $ $ $\n";
    outfile << "$ $ $ $ $ 7 $ $ 1\n";
    outfile << "9 $ 4 $ $ $ $ 6 $\n";
    outfile << "$ $ $ 2 $ $ $ $ 3\n";
    outfile << "$ $ $ $ $ $ 2 8 7\n";
    outfile << "-----------------\n";
    outfile.close();

    // Read the file
    vector<Board> result = readFile(file_path);

    // Verify the result
    ASSERT_EQ(2, result.size());

    Board expected_board1 = {
            {'5', '3', '$', '$', '7', '$', '$', '$', '$'},
            {'6', '$', '$', '1', '9', '5', '$', '$', '$'},
            {'$', '9', '8', '$', '$', '$', '$', '6', '$'},
            {'8', '$', '$', '$', '6', '$', '$', '$', '3'},
            {'4', '$', '$', '8', '$', '3', '$', '$', '1'},
            {'7', '$', '$', '$', '2', '$', '$', '$', '6'},
            {'$', '6', '$', '$', '$', '$', '2', '8', '$'},
            {'$', '$', '$', '4', '1', '9', '$', '$', '5'},
            {'$', '$', '$', '$', '8', '$', '$', '7', '9'}
    };

    Board expected_board2 = {
            {'1', '$', '$', '$', '2', '$', '$', '$', '$'},
            {'2', '$', '$', '$', '1', '$', '7', '$', '$'},
            {'$', '$', '$', '3', '$', '$', '$', '$', '8'},
            {'$', '$', '9', '$', '$', '$', '$', '$', '$'},
            {'5', '1', '8', '$', '6', '3', '$', '$', '$'},
            {'$', '$', '$', '$', '$', '7', '$', '$', '1'},
            {'9', '$', '4', '$', '$', '$', '$', '6', '$'},
            {'$', '$', '$', '2', '$', '$', '$', '$', '3'},
            {'$', '$', '$', '$', '$', '$', '2', '8', '7'}
    };

    ASSERT_EQ(expected_board1, result[0]);
    ASSERT_EQ(expected_board2, result[1]);
}

TEST_F(FileTest, WriteFile) {
    // Prepare test data
    vector<Board> boards = {
            {
                    {'5', '3', '$', '$', '7', '$', '$', '$', '$'},
                    {'6', '$', '$', '1', '9', '5', '$', '$', '$'},
                    {'$', '9', '8', '$', '$', '$', '$', '6', '$'},
                    {'8', '$', '$', '$', '6', '$', '$', '$', '3'},
                    {'4', '$', '$', '8', '$', '3', '$', '$', '1'},
                    {'7', '$', '$', '$', '2', '$', '$', '$', '6'},
                    {'$', '6', '$', '$', '$', '$', '2', '8', '$'},
                    {'$', '$', '$', '4', '1', '9', '$', '$', '5'},
                    {'$', '$', '$', '$', '8', '$', '$', '7', '9'}
            },
            {
                    {'1', '$', '$', '$', '2', '$', '$', '$', '$'},
                    {'2', '$', '$', '$', '1', '$', '7', '$', '$'},
                    {'$', '$', '$', '3', '$', '$', '$', '$', '8'},
                    {'$', '$', '9', '$', '$', '$', '$', '$', '$'},
                    {'5', '1', '8', '$', '6', '3', '$', '$', '$'},
                    {'$', '$', '$', '$', '$', '7', '$', '$', '1'},
                    {'9', '$', '4', '$', '$', '$', '$', '6', '$'},
                    {'$', '$', '$', '2', '$', '$', '$', '$', '3'},
                    {'$', '$', '$', '$', '$', '$', '2', '8', '7'}
            }
    };

    // Write the data to file
    std::ofstream outfile("test_output.txt");
    writeFile(boards, outfile);
    outfile.close();

    // Read the written file
    std::ifstream infile("test_output.txt");
    std::string line;
    std::vector<std::string> file_lines;

    while (std::getline(infile, line)) {
        file_lines.push_back(line);
    }
    infile.close();

    // Verify the file content
    std::stringstream expected_content;
    expected_content << "5 3 $ $ 7 $ $ $ $ " << std::endl;
    expected_content << "6 $ $ 1 9 5 $ $ $ " << std::endl;
    expected_content << "$ 9 8 $ $ $ $ 6 $ " << std::endl;
    expected_content << "8 $ $ $ 6 $ $ $ 3 " << std::endl;
    expected_content << "4 $ $ 8 $ 3 $ $ 1 " << std::endl;
    expected_content << "7 $ $ $ 2 $ $ $ 6 " << std::endl;
    expected_content << "$ 6 $ $ $ $ 2 8 $ " << std::endl;
    expected_content << "$ $ $ 4 1 9 $ $ 5 " << std::endl;
    expected_content << "$ $ $ $ 8 $ $ 7 9 " << std::endl;
    expected_content << "-----------------" << std::endl;
    expected_content << "1 $ $ $ 2 $ $ $ $ " << std::endl;
    expected_content << "2 $ $ $ 1 $ 7 $ $ " << std::endl;
    expected_content << "$ $ $ 3 $ $ $ $ 8 " << std::endl;
    expected_content << "$ $ 9 $ $ $ $ $ $ " << std::endl;
    expected_content << "5 1 8 $ 6 3 $ $ $ " << std::endl;
    expected_content << "$ $ $ $ $ 7 $ $ 1 " << std::endl;
    expected_content << "9 $ 4 $ $ $ $ 6 $ " << std::endl;
    expected_content << "$ $ $ 2 $ $ $ $ 3 " << std::endl;
    expected_content << "$ $ $ $ $ $ 2 8 7 " << std::endl;
    expected_content << "-----------------"  << std::endl;

    std::stringstream actual_content;
    for (const auto& line : file_lines) {
        actual_content << line << std::endl;
    }

    ASSERT_EQ(expected_content.str(), actual_content.str());
}

TEST_F(SudokuTest, GenerateGame) {
    // Set up test data
    int gameNum = 5;
    vector<int> digCount = {30, 40};
    bool mustUnique = true;

    // Generate games
    std::ofstream outfile("test_generate_game.txt");
    generateGame(gameNum, digCount, outfile, sudoku, mustUnique);
    outfile.close();

    // Read the generated file
    vector<Board> generated_boards = readFile("test_generate_game.txt");

    // Verify the number of generated boards
    ASSERT_EQ(gameNum, generated_boards.size());

    // Verify the number of filled and empty cells in each board
    for (const auto& board : generated_boards) {
        int filledCount = 0;
        int emptyCount = 0;

        for (const auto& row : board) {
            for (const auto& cell : row) {
                if (cell != '$') {
                    filledCount++;
                } else {
                    emptyCount++;
                }
            }
        }

        // Verify the number of filled and empty cells falls within the specified range
        ASSERT_GE(emptyCount, digCount[0]);
        ASSERT_LE(emptyCount, digCount[1]);
        ASSERT_EQ(emptyCount, 81 - filledCount);
    }
}

int main(int argc, char** argv) {
    ::testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}
